/*******************************************************************************
 * Copyright (c) 2014 Francesco Gabbrielli
 * 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Contributors:
 *     Francesco Gabbrielli - initial API and implementation
 ******************************************************************************/
package it.francescogabbrielli.android.solidalapp.response;

import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import it.francescogabbrielli.android.solidalapp.AbstractMapFragment;
import it.francescogabbrielli.android.solidalapp.Constants;
import it.francescogabbrielli.android.solidalapp.DataEvent;
import it.francescogabbrielli.android.solidalapp.EndpointsListLoader;
import it.francescogabbrielli.android.solidalapp.MapUtils;
import it.francescogabbrielli.android.solidalapp.R;
import it.francescogabbrielli.android.solidalapp.SolidalApp;
import it.francescogabbrielli.android.solidalapp.SolidalApp.DataKey;
import it.francescogabbrielli.android.solidalapp.deviceendpoint.model.Device;
import it.francescogabbrielli.android.solidalapp.requestendpoint.model.Request;
import it.francescogabbrielli.android.solidalapp.responseendpoint.model.Response;

import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;

import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
import android.util.Log;

/**
 * 
 */
public class ResponseMapFragment extends AbstractMapFragment<ResponseFragmentListener> 
		implements LoaderCallbacks<List<Request>> {
	
	private long blinkId, simulatedId, showingId, feedbackId;
	
	private Response sending;
	
	
	class CheckExpiry extends TimerTask {
		
		@Override
		public void run() {
			
			try {
				boolean atLeastOne = false;
				for (final Marker m : getRequestMarkers().keySet()) {
					final Request req = getRequestMarkers().get(m);
					boolean isAccepted = req.getAccepted()!=null && req.getAccepted();
					if (!isAccepted)
						atLeastOne = true;
					if (!isAccepted && req.getExpiry()<System.currentTimeMillis()) {
						new Handler(app.getMainLooper()).post(new Runnable() {
							@Override
							public void run() {
								req.setExpired(true);
								updateMarker(m);
							}
						});
					}
				}
				if (!atLeastOne)
					descheduleCheckExpiry();
			} catch(Exception e) {
				Log.e(SolidalApp.TAG, "Error checking expiry", e);
			}
		}
	}
	
	private Timer checkExpiry;
	
	private void scheduleCheckExpiry() {
		if (checkExpiry==null) {
			checkExpiry = new Timer();
			checkExpiry.scheduleAtFixedRate(new CheckExpiry(), 600, 60000);
		}
	}
	
	private void descheduleCheckExpiry() {
		if (checkExpiry!=null) {
			checkExpiry.cancel();
			checkExpiry = null;
		}
	}

	@Override
	public void onStart() {
		super.onStart();
//		try {
//			if (blinkId==0)
//				blinkId = Long.parseLong(getActivity().getIntent().getStringExtra("id"));
//		} catch(Exception e) {
//			Log.v(SolidalApp.TAG, "Error parsing request id: " + e);
//		}
	}
	
//	@Override
//	public void onStatusUpdate(StatusEvent event) {
//		super.onStatusUpdate(event);
//		
//		switch (event.getType()) {
//		case GCM:
//			reload(false);
//			break;
//
//		default:
//			break;
//		}
//	}
	
	private Marker getMarker(long id) {
		for(Marker marker : getRequestMarkers().keySet())
			if (id == getRequestMarkers().get(marker).getId())
				return marker;
		return null;
	}
	
	@Override
	public void onDataChanged(DataEvent event) {
		
		if(map==null || !isAdded())
			return;
		
		super.onDataChanged(event);
		
		Marker mine = getMarker();
		
		switch(event.getKey()) {
			
		// The address text has changed by reverse lookup

		case ADDRESS_TEXT:
			if (mine!=null) {
				mine.setTitle((String) app.getData(DataKey.ADDRESS_TEXT));
				mine.setSnippet(getString(R.string.map_current_address));
				if (mapShowInfo && showingId==0)
					mine.showInfoWindow();
			}
			break;

		// The response has been sent or is sending	
		case RESPONSE:
			
			// Search the current marker
			Response response = (Response) app.getData(DataKey.RESPONSE);
			if (response == null)// trick for recovering the current sending response
				response = sending;
			Marker m = null;
			if (response!=null && response.getRequestId()!=null)
				m = getMarker(response.getRequestId().longValue());
			
			// update map
			if (mine!=null)
				mine.setDraggable(response==null);
			if (response==null || (response!=null && response.getId()!=null))
				reload(event.isTriggered());
			else if (m!=null)
				updateMarker(m);
			
			break;
			
		default:
		}
	}	
	
	@Override
	public void onActivityCreated(Bundle savedInstanceState) {
		showingId = savedInstanceState!=null ? savedInstanceState.getLong("showing", 0) : 0;
		feedbackId = savedInstanceState!=null ? savedInstanceState.getLong("feedback", 0) : 0;
		super.onActivityCreated(savedInstanceState);
	}
		
	@Override
	public void onSaveInstanceState(Bundle outState) {
		super.onSaveInstanceState(outState);
		outState.putLong("showing", showingId);
		outState.putLong("feedback", feedbackId);
	}
	
	@Override
	public Loader<List<Request>> onCreateLoader(int id, Bundle data) {
		return new EndpointsListLoader<Request>(getActivity(), Request.class)
			.setFilters(data);
	}

	@Override
	public void onLoadFinished(Loader<List<Request>> loader, List<Request> data) {

		try {
			
			releaseRequestMarkers();
			
			boolean atLeastOne = false;
			
			boolean blinking = false;
			Marker togo = null;
			Marker mine = getMarker();
			
			Response res = (Response) app.getData(DataKey.RESPONSE);

			double east = mine.getPosition().longitude;
			double west = east;
			double north = mine.getPosition().latitude;
			double south = north;
			
			if (data!=null)
				
				for(Request r : data) {
					
					if (r.getExpired())
						continue;
	
					if (r.getAccepted()==null || !r.getAccepted())
						atLeastOne = true;
					
					west = Math.min(west, r.getLongitude());
					east = Math.max(east, r.getLongitude());
					south = Math.min(south, r.getLatitude());
					north = Math.max(north, r.getLatitude());
						
					Marker m = map.addMarker(new MarkerOptions()
				 			.position(new LatLng(r.getLatitude(), r.getLongitude()))
				 			.snippet(trunk(r.getMessage()))
				 			.title(r.getDetails())
				 			);
					
					getRequestMarkers().put(m, r);
					
					// Set icon (color)
					boolean canBlink = blinkId==r.getId();
					updateMarker(m);
					if (res!=null && res.getRequestId()==blinkId)
						canBlink = false;
					
					// Blink the incoming request if any
					if (canBlink && !r.getExpired() && !r.getCanceled()) {
						togo = m;
						showingId = blinkId;
						blinking = true;
						MapUtils.startBlinkIcon(loader.getContext(), m, 333, 3000, new float [] {
							BitmapDescriptorFactory.HUE_VIOLET,
							BitmapDescriptorFactory.HUE_YELLOW
						});
					}
					
					if (!blinking) {
						// Show current marker 
						if (showingId == r.getId())
							togo = m;
						// Show current responded request
						if (showingId==0 && res!=null && r.getId().equals(res.getRequestId()))
							togo = m;
					}
				}

			if (togo==null)
				togo = mine;
			
			if (togo!=null) {
				mapShowInfo = true;
				SharedPreferences prefs = getSherlockActivity().getSharedPreferences(
						SolidalApp.class.getCanonicalName(), Context.MODE_PRIVATE);
				if ("ALL".equals(prefs.getString(Constants.PREF_NOTIFY, "NONE")) &&
						(east-west>0.05d || north-south>0.05d))
					centerPosition(new LatLngBounds(
							new LatLng(south, west),
							new LatLng(north, east)));
				else
					centerPosition(togo);
				togo.showInfoWindow();
			}
			
			simulatedId = blinkId;
			blinkId = 0;
			
			if (atLeastOne)
				scheduleCheckExpiry();
			else
				descheduleCheckExpiry();
			
		} finally {
		
			interactionListener.setLoading(false);
			
		}

	}

	@Override
	public void onLoaderReset(Loader<List<Request>> loader) {
		
	}
	
	@Override
	public void onMapClick(LatLng pos) {
		showingId = 0;
		super.onMapClick(pos);
	}
	
	@Override
	public boolean onMarkerClick(Marker marker) {
		try {
			showingId = getRequestMarkers().get(marker).getId();
		} catch(Exception e) {
			showingId = 0;
		}
		return super.onMarkerClick(marker);
	}

	@Override
	public void onInfoWindowClick(Marker marker) {
		if (getRequestMarkers().containsKey(marker))
			interactionListener.showRequest(getRequestMarkers().get(marker));
	}
	
	/**
	 * Invoked on incoming requests (via GCM)
	 * 
	 * @param requestData
	 * 		the data of the request in free format
	 */
	void onNewRequest(Bundle requestData) {
		blinkId = 0;
		try {
			blinkId = Long.parseLong(requestData.getString("id"));
		} catch(Exception e) {}
		reload(true);
	}
	
	/**
	 * Invoked on an incoming feedback for the current response (via GCM)
	 * 
	 * @param reqId 
	 * 		the id of the request responded by the current response 
	 */
	void onFeedback(long reqId) {
		feedbackId = reqId;
		reload(true);
	}
	
	void reload(boolean restart) {
		Device device = (Device) app.getData(DataKey.DEVICE);
		if (device!=null) {
			interactionListener.setLoading(true);
			Bundle filters = new Bundle();
			filters.putLong("deviceId", device.getId());
			if (restart)
				getLoaderManager().restartLoader(Constants.LOADER_REQUEST_LIST, filters, this);
			else
				getLoaderManager().initLoader(Constants.LOADER_REQUEST_LIST, filters, this);
		}
	}
	
	private String trunk(String text) {
		if(text==null || text.length()<30)
			return text;
		else
			return text.substring(0,27) + "...";
	}
	
	private void updateMarker(Marker m) {
		
		Request request = getRequestMarkers().get(m);
		if (request==null)
			return;
		if (request.getId()==feedbackId)
			request.setCompleted(true);

		float hue = BitmapDescriptorFactory.HUE_YELLOW;
		
		Response response = (Response) app.getData(DataKey.RESPONSE);
			
		// marker not selected or no response at all
		if (response==null || !request.getId().equals(response.getRequestId())) {
			
			if (request.getAccepted())
				hue = BitmapDescriptorFactory.HUE_BLUE;
			if (request.getCanceled())
				hue = BitmapDescriptorFactory.HUE_MAGENTA;
			if (request.getExpired())
				hue = BitmapDescriptorFactory.HUE_VIOLET;
			if (request.getCompleted())
				hue = BitmapDescriptorFactory.HUE_AZURE;
						
			MapUtils.stopBlinkIcon(m, hue);
			
		// selected and sending
		} else if(response.getId()==null) {
			
			sending = response;
			
			MapUtils.startBlinkIcon(getActivity(), m, 900, Long.MAX_VALUE, new float[] {
				BitmapDescriptorFactory.HUE_GREEN,
				BitmapDescriptorFactory.HUE_YELLOW
			});
			
		// selected and sent
		} else {
			
			sending = null;
			
			MapUtils.stopBlinkIcon(m, response.getCompleted() 
					? BitmapDescriptorFactory.HUE_AZURE
					: BitmapDescriptorFactory.HUE_GREEN);
			
		}
		
		if(mapShowInfo)
			m.showInfoWindow();

	}
	
	Long getSimulationRequestId() {
		Long ret = Long.valueOf(simulatedId);
		simulatedId = 0;
		return ret;
//		long max = 0;
//		Long ret = Long.valueOf(0);
//		for (Request r : getRequestMarkers().values()) {
//			if(max<r.getTimestamp()) {
//				max = r.getTimestamp();
//				ret = r.getRequestKey().getId();
//			}
//		}
//		return ret;
	}
	
}
